package com.levi.juc.waitnotify;

import java.util.ArrayList;
import java.util.List;

/**
 * 该机制介绍:
 * 	object提供了wait(), notify(), notifyAll(),这是多线程的基础;
 * 	等待/通知机制就是基于上面三种方法实现,
 * 	在一个线程内调用该线程锁对象的wait()方法,线程进入等待队列,等待被唤醒;
 *
 * 这里需要注意:
 * 	wait()和notify()方法需要结合线程同步(synchronized)使用;
 * 	wait()会释放锁,notify()不会释放锁;
 * 	即: 使用notify()只有当当前线程结束,才会执行被唤醒的线程.
 * 	notify()	唤醒在此对象监视器上等待的一个线程;
 * 	notifyAll()	唤醒在此对象监视器上等待的所有线程.
 *
 * 此处可能出现的异常:
 * 	IllegalMonitorStateException： if the current thread is not the owner of this object’s monitor；
 * 	当前线程不是此对象监视器的所有者.
 */

public class NotifyTest {
    public static void main(String[] args) {
        ThreadB tb = new ThreadB();
        Thread t1 = new Thread(tb);
        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        ThreadA ta = new ThreadA();
        Thread t2 = new Thread(ta);
        t2.start();
    }
}
class Data {
    static Object lock = new Object();
    static List<String> list = new ArrayList<>();
}
class ThreadA implements Runnable {
    @Override
    public void run() {
        synchronized (Data.lock) {
            for (int i = 1; i <= 10; i++) {
                Data.list.add("abc");
                System.out.println("线程A向list中添加一个元素，此时list中的元素个数为：" + Data.list.size());
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // fixme Data.list.size() = 5的时候Data.lock已经被唤醒了，但是要等当前线程执行完,即notify不会释放锁
                if(Data.list.size() == 5){
                    System.out.println("达到5了，开始notify");
                    Data.lock.notify();
                }
            }
        }
    }
}
class ThreadB implements Runnable {
    @Override
    public void run() {
        while (true) {
            synchronized (Data.lock) {
                if(Data.list.size() != 5){
                    try {
                        // fixme wait会释放锁
                        Data.lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程B收到通知，开始执行自己的业务...");
                    break;
                }
            }
        }
    }
}

